/**
 * 
 */
package org.going.invoker;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.going.annotation.AfterInvoke;
import org.going.annotation.BeforeInvoke;
import org.going.factory.JSHandlerFactory;
import org.going.factory.ParamValueGeneratorFactory;
import org.going.utils.ClassUtils;

/**
 * @author ferendo.huang@gmail.com
 * 
 */
public class WebFunctionImpl implements WebFunction
{
	private Object obj;
	private Method method;
	private WebFunctionCallback funcCallback;
	private BeforeInspector before;
	private AfterInspector after;

	private static Log log = LogFactory.getLog(WebFunctionImpl.class);

	public WebFunctionImpl(Object obj, Method method) throws InstantiationException, IllegalAccessException
	{
		super();
		this.obj = obj;
		this.method = method;
		BeforeInvoke beforeInvoke = this.method.getAnnotation(BeforeInvoke.class);
		if (beforeInvoke != null)
		{
			this.before = ClassUtils.getBeforeInspector(beforeInvoke.beforeInspector());
		}
		AfterInvoke afterInvoke = this.method.getAnnotation(AfterInvoke.class);
		if (afterInvoke != null)
		{
			this.after = ClassUtils.getAfterInspector(afterInvoke.afterInspector());
		}
	}

	public Object getObj()
	{
		return obj;
	}

	public void setObj(Object obj)
	{
		this.obj = obj;
	}

	public Method getMethod()
	{
		return method;
	}

	public void setMethod(Method method)
	{
		this.method = method;
	}

	/**
	 * Handle request for web object...
	 * 
	 * @param response
	 * @param request
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public void invoke(final HttpServletRequest request, final HttpServletResponse response)
			throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
	{
		Class<?>[] parameterTypes = this.method.getParameterTypes();
		Object[] objs = new Object[parameterTypes.length];
		for (int i = 0; i < parameterTypes.length; i++)
		{
			Class<?> clazz = parameterTypes[i];
			if (WebFunctionCallback.class.equals(clazz))
			{
				if (funcCallback == null)
				{
					synchronized (this)
					{
						if (funcCallback == null)
						{
							// initialize callback function...
							funcCallback = new WebFunctionCallback()
							{
								@Override
								public void setVal(String id, Object val)
								{
									log.info("id:" + id + ",value:" + val + ", param: " + request.getParameter("test"));
									JSHandlerFactory.getCommonJSHandler().setValById(id, val, request, response);
								}

								@Override
								public void evalJS(String jsCode)
								{
									log.info("jscode:" + jsCode);
									JSHandlerFactory.getCommonJSHandler().evalJS(jsCode, request, response);
								}
							};
						}
					}
				}
				objs[i] = funcCallback;
			}
			else
			{
				try
				{
					// set parameter value
					objs[i] = ParamValueGeneratorFactory.getValueGenerator(clazz).generate(String.valueOf(i), clazz,
							request);
				}
				catch (ParamValueGenerateException e)
				{
					// do something...
					log.error(" It is fail to generate the object value of parameter index :: " + i, e);
					throw new InvocationTargetException(e);
				}
			}
		}
		if (before != null)
		{
			before.inspect(this.method, objs);
		}
		Object retObj = this.method.invoke(obj, objs);
		if (after != null)
		{
			after.inspect(this.method, objs, retObj);
		}
	}
}
